package com.main.dao;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;

import org.dom4j.tree.AbstractEntity;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class AbstractDAO<T extends AbstractEntity> {

    @PersistenceContext
    protected EntityManager em;

    private Class<T> entityClass;

	public AbstractDAO(final Class<T> clazz) {
    	entityClass = clazz;
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public T find(Long key){
        return em.find(entityClass, key);
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<T> findAll(){
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(entityClass);
        cq.select( cq.from(entityClass));
        return em.createQuery( cq ).getResultList();
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<T> findAll(int firstResult, int maxResults){
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(entityClass);
        cq.select( cq.from( entityClass ) );
        return em.createQuery( cq ).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<T> findRange(int from, int to){
        return findAll( from, ( to - from ) );
    }

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
     public void clear(){
        em.clear();
    }

    public void flush(){
        em.flush();
    }

    public T merge(T entity){
        return em.merge(entity);
    }

    public void persist(T entity){
        em.persist( entity );
    }
    
    public void remove(T entity){
        em.remove( em.merge(entity) );
    }

    @SuppressWarnings("unchecked")
	public Class<T> getEntityClass() {
        if(entityClass == null){
            Type type = this.getClass().getGenericSuperclass();
            ParameterizedType parameterizedType = (ParameterizedType) type;
            entityClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];
        }
        
        return entityClass;
    }
}
